<?php
/**
 * Parser Generator for PHP
 *
 * LICENSE
 *
 * This source file is subject to the MIT license that is bundled
 * with this package in the file LICENSE.
 *
 * @package    MC_Parser
 * @author     Chadwick Morris <chad@mailchimp.com>
 * @license    http://www.opensource.org/licenses/mit-license.php
 * @version    0.1
 */

require_once 'MC/Parser/Def/Empty.php';
require_once 'MC/Parser/Def/Literal.php';
require_once 'MC/Parser/Def/NOrMore.php';
require_once 'MC/Parser/Def/OneOf.php';
require_once 'MC/Parser/Def/Recursive.php';
require_once 'MC/Parser/Def/Regex.php';
require_once 'MC/Parser/Def/Set.php';
require_once 'MC/Parser/Def/Word.php';


/**
 * The root class of all instances generated by MC_Parser when defining a grammar
 */
abstract class MC_Parser_Def {
    
    public $name = null;
    public $suppress = false;
    
    /**
     * Parse a string, and return the result or throw a parser exception
     * @param string $str
     * @return MC_Parser_Token
     */
    public function parse($str) {
        $str = ltrim($str);
        
        list($loc, $tok) = $this->parsePart($str, 0);
        if($loc != strlen($str)) {
            throw new MC_Parser_ParseError('An error occurred: "' . substr($str, $loc) . '"', $str, $loc);
        }
        return $tok;
    }
    
    /**
     * Parse a string, cleaning up whitespace when we're done
     * @param string $str
     * @param integer $loc
     * @return array A two-item array of the string location where parsing stopped, and the MC_Token instance that matches the grammar conditions
     */
    public function parsePart($str, $loc) {
        list($loc, $tok) = $this->_parse($str, $loc);
        
        $char = $str[$loc++];
        while($char && MC_Parser::isWhitespace($char)) {
            $char = $str[$loc++];
        }
        --$loc;

        return array($loc, $tok);
    }
    
    /**
     * Each definition type should implement their own _parse() function that tests a string
     * @param string $str the string to parse
     * @param integer $loc the index to start parsing
     * @return array A two-item array of the string location where parsing stopped, and the MC_Token instance that matches the grammar conditions
     */
    abstract public function _parse($str, $loc);
    
    /**
     * Each definition type should have some way of giving itself a name if the user didn't provide one
     * @return string
     */
    abstract public function _name();
    
    /**
     * Return the user-provided name, if given, or the generated one otherwise
     */
    public function getName() {
        if($this->name !== null) return $this->name;
        return $this->_name();
    }
    
    /**
     * Give this grammar def a name in the results
     * @param string $name
     * @return MC_Parser_Def $this - chainable method
     */
    public function name($name) {
        $this->name = $name;
        return $this;
    }
    
    /**
     * Toggle suppressing the token from the results
     */
    public function suppress() {
        $this->suppress = true;
        return $this;
    }
    
    /**
     * Return a token instance, copying over this Def's name and flagging suppression
     * @return MC_Parser_Token|null the new token, or null if the token should be suppressed
     */
    public function token($value) {
        if($this->suppress) return null;
        return new MC_Parser_Token($value, $this->name);
    }
    
    /**
     * Return a token group (a token that can contain subtokens), copying over this Def's name
     * token groups cannot be suppressed
     * @return MC_Parser_Token_Group
     */
    public function tokenGroup() {
        return new MC_Parser_Token_Group($this->name);
    }
}

?>
